package com.baymax.test.scene.demo

import android.Manifest
import android.content.Context
import android.content.IntentFilter
import android.hardware.usb.UsbConstants
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbInterface
import android.hardware.usb.UsbManager
import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioPlaybackCaptureConfiguration
import android.media.AudioRecord
import android.media.AudioTimestamp
import android.media.AudioTrack
import android.media.MediaCodec
import android.media.MediaMuxer
import android.media.MediaRecorder
import android.media.projection.MediaProjection
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.baymax.test.scene.BaseGroupScene
import com.baymax.test.scene.PlayerTestScene
import com.baymax.test.scene.addButton
import com.baymax.test.scene.addDFView
import com.baymax.test.scene.demo.maverick.sceneViewModel
import com.baymax.test.scene.dump
import com.baymax.test.scene.newSceneFlexContainer
import com.bytedance.scene.ktx.replace
import com.bytedance.scene.ktx.requireNavigationScene
import com.tbruyelle.rxpermissions2.RxPermissions
import com.ysten.libs.changba.ChangBaSdk
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import okio.Buffer
import okio.Okio
import okio.Pipe
import okio.Sink
import okio.Source
import okio.Timeout
import okio.blackholeSink
import okio.buffer
import okio.sink
import okio.source
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File


class AudioRecorderScene : BaseGroupScene() {
    private val viewModel: RecorderVM by sceneViewModel()

    @OptIn(ExperimentalCoroutinesApi::class)
    override fun onCreateView(
        p0: LayoutInflater,
        p1: ViewGroup,
        p2: Bundle?
    ) = p1.newSceneFlexContainer() {
        RxPermissions(requireActivity()).request(Manifest.permission.RECORD_AUDIO) .subscribe()
        val ob = object : android.content.BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: android.content.Intent) {
                intent.dump()
                val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
                if (device != null) {
                    println("id=${device.deviceId} device=$device")
                }
            }
        }
        requireSceneContext().registerReceiver(ob, IntentFilter().apply {
            addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
            addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
            addAction("android.media.VOLUME_CHANGED_ACTION")
        })
        lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onDestroy(owner: LifecycleOwner) {
                super.onDestroy(owner)
                requireSceneContext().unregisterReceiver(ob)
            }
        })
        addButton("find record device") {
            println("record device")
            val usbManager =
                requireSceneContext().getSystemService(Context.USB_SERVICE) as UsbManager
            usbManager.deviceList.forEach { (k, v) ->
                println("device $k -> $v")
                findInterface(v)?.let { println("find $it") }
            }
        }

        val d = addButton("record-${viewModel.init.record}") {
            launch {
                if (viewModel.awaitState().record) {
                    viewModel.stopRecord()
                } else {
                    viewModel.record(newRecoderL23(requireSceneContext()))
                }
            }
        }
        val d2 = addButton("record-sys-${viewModel.init.record}") {
            launch {
                if (viewModel.awaitState().sysRecord) {
                    viewModel.stopSys()
                } else {
                    viewModel.startSys()

                }
            }
        }

        addButton("play pcm") {
            viewModel.startAudioTrack(viewModel.createTrack())
            viewModel.playTestPcm()
        }
        viewModel.onEach { st ->
            d.text = "record-${st.record}"
            d2.text = "record-sys-${st.sysRecord}"
        }
        addButton("show player") {
            val p = PlayerTestScene()
            replace(0xf, p, "player")
            requireNavigationScene().addOnBackPressedListener(p) {
                remove(p)
                true
            }
        }


        val fr = addDFView(-2, -2, FrameLayout(requireSceneContext()))
        fr.id = 0xf

    }

    @RequiresPermission(Manifest.permission.RECORD_AUDIO)
    private fun newRecoderL23(context: Context): AudioRecord {
        // 采样率（常用 44100Hz）
        val sampleRate = 48000
// 声道配置（单声道）
        val channelConfig = AudioFormat.CHANNEL_OUT_STEREO

// 音频格式（16 位 PCM）
        val audioFormat = AudioFormat.ENCODING_PCM_16BIT

// 计算缓冲区大小
        val bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
        val audioRecord = AudioRecord(
            MediaRecorder.AudioSource.DEFAULT,
            sampleRate,
            channelConfig,
            audioFormat,
            bufferSize
        )
        return audioRecord
    }


    private fun findInterface(usbDevice: UsbDevice): UsbInterface? {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            (0 until usbDevice.configurationCount).forEach { cc ->
                val conf = usbDevice.getConfiguration(cc)
                (0 until conf.interfaceCount).forEach {
                    val usbInterface = conf.getInterface(it)
                    val hasAudio = usbInterface.interfaceClass == UsbConstants.USB_CLASS_AUDIO
                            && usbInterface.interfaceSubclass == UsbConstants.USB_CLASS_COMM
                            && usbInterface.endpointCount > 0
                    if (hasAudio) {
                        if ((0 until usbInterface.endpointCount).any {
                                val d = usbInterface.getEndpoint(it)
                                (d.direction == UsbConstants.USB_DIR_IN)
                            }) {
                            return usbInterface
                        }
                    }
                }
            }
        } else {
            (0 until usbDevice.interfaceCount).forEach {
                val usbInterface = usbDevice.getInterface(it)
                val hasAudio = usbInterface.interfaceClass == UsbConstants.USB_CLASS_AUDIO
                        && usbInterface.interfaceSubclass == UsbConstants.USB_CLASS_COMM
                        && usbInterface.endpointCount > 0

                if (hasAudio) {
                    if ((0 until usbInterface.endpointCount).any {
                            val d = usbInterface.getEndpoint(it)
                            (d.direction == UsbConstants.USB_DIR_IN)
                        }) {
                        return usbInterface
                    }
                }
            }
        }

        return null
    }

    class RecorderVM(val init: State) :
        MavericksViewModel<RecorderVM.State>(init), KoinComponent {

        private val context by inject<Context>()
        private val sysMirco by lazy { ChangBaSdk.initMicroManager(context) }

        data class State(val record: Boolean = false, val sysRecord: Boolean = false) :
            MavericksState

        val worker = Dispatchers.IO.limitedParallelism(1)
        val sampleRate = 48000
        val pipe = Pipe(1024 * 8)
        val channelConfig = AudioFormat.CHANNEL_OUT_STEREO
        val audioFormat = AudioFormat.ENCODING_PCM_16BIT

        fun startSys() {
            setState { this.copy(false, true) }
            sysMirco.startRecord(null)
            val ba = ByteArray(4096)
            val f = File(context.getExternalFilesDir(""), "test.pcm").sink().buffer()
            viewModelScope.launch(Dispatchers.IO.limitedParallelism(1)) {
                while (sysMirco.isRecoding()) {
                    yield()
                    val s = sysMirco.readMicroData(ba)
                    f.write(ba, 0, s)
                    delay(20)
                }
                println("release")
                f.close()
            }
        }

        fun stopSys() {
            setState { this.copy(false, false) }
            sysMirco.stopRecord()
        }

        fun record(recorder: AudioRecord) {
            setState { this.copy(true, false) }
            val track = createTrack()
            startAudioTrack(track)
            viewModelScope.launch(Dispatchers.IO.limitedParallelism(1)) {
                recorder.startRecording()
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    val timestamp = AudioTimestamp()
                    val ts =
                        recorder.getTimestamp(timestamp, AudioTimestamp.TIMEBASE_MONOTONIC)
                    if (ts == AudioRecord.SUCCESS) {
                        val framePosition = timestamp.framePosition // 当前帧位置
                        val nanoTime = timestamp.nanoTime // 对应的时间戳（纳秒）

                        // 计算当前时间与硬件时间的差值
                        val currentNanoTime = System.nanoTime()
                        val latencyMs = (currentNanoTime - nanoTime) / 1000000.0
                        println("硬件延迟: " + latencyMs + " ms")
                    }
                    println("ts:$ts from ${recorder.audioSource} ,${recorder.notificationMarkerPosition}")
                }
                println("start record")
                val f = File(context.getExternalFilesDir(""), "test.pcm").sink().buffer()
                val bufferSize = AudioRecord.getMinBufferSize(
                    48000,
                    AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT
                )
                println("minSize $bufferSize")
                val bt = ByteArray(bufferSize)
                val pipeSink = pipe.sink.buffer()

                while (awaitState().record) {
                    println("read state =${recorder.state}  ,${recorder.notificationMarkerPosition}")
//                            if (recorder.state == AudioRecord.STATE_INITIALIZED) {
//                                recorder.startRecording()
//                            }
//                            if (recorder.state == AudioRecord.RECORDSTATE_RECORDING) {
                    val s = recorder.read(bt, 0, bufferSize)
                    pipeSink.write(bt, 0, bufferSize)
                    f.write(bt, 0, s)
                    println("read $s ")
                    delay(50)
                }
                println("release")
                recorder.stop()
                recorder.release()
                f.close()
            }

        }

        fun stopRecord() {
            setState { this.copy(false) }
        }

        fun startAudioTrack(audioTrack: AudioTrack) {
            viewModelScope.launch(worker) {
                Log.d("Audio", "AudioTrack play ${audioTrack.state} ")
                if (audioTrack.state == AudioTrack.STATE_INITIALIZED) {
                    if (audioTrack.playState != AudioTrack.PLAYSTATE_PLAYING) {
                        audioTrack.play()
                    }
                }
                val audioSink = object : Sink {
                    val cursor = Buffer.UnsafeCursor()
                    override fun close() {
                        Log.d("Audio", "player close")
                        audioTrack.stop()
                    }

                    override fun flush() {
                    }

                    override fun timeout(): Timeout = Timeout.NONE

                    override fun write(source: Buffer, byteCount: Long) {
                        Log.d(
                            "AudioSink",
                            "AudioTrack write ${source.size} ,bl=$byteCount ,]"
                        )
                        var readUnsafe = source.readUnsafe(cursor)
                        val l = readUnsafe.next()
                        Log.d("AudioSink", "readUnsafe $readUnsafe ${readUnsafe.start} , ${readUnsafe.end} ,l=$l")
                        audioTrack.write(cursor.data!! , cursor.start , l )
                        source.skip(byteCount)
                        readUnsafe.close()
                    }
                }
                val bf = pipe.source.buffer()
                while (isActive) {
                    bf.readAll(audioSink)
                }
            }

        }


        override fun onCleared() {
            super.onCleared()
            sysMirco.dispose()
        }

        companion object : MavericksViewModelFactory<RecorderVM, State> {
            override fun create(
                viewModelContext: ViewModelContext, state: State
            ): RecorderVM {
                return RecorderVM(state)
            }

            override fun initialState(viewModelContext: ViewModelContext): State? {
                return RecorderVM.State(false)
            }
        }

        fun createTrack(): AudioTrack {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                val audioAttributes = AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // 改为speech可能更适合微信语音
                    .build()
                val format = AudioFormat.Builder()
                    .setSampleRate(sampleRate)
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                    .setChannelMask(AudioFormat.CHANNEL_IN_STEREO)
                    .build()
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    AudioTrack.Builder()
                        .setAudioAttributes(audioAttributes)
                        .setAudioFormat(format)
                        .setBufferSizeInBytes(9600)
                        .setTransferMode(AudioTrack.MODE_STREAM)
                        .build()
                } else {
                    AudioTrack(
                        AudioManager.STREAM_MUSIC,
                        sampleRate,
                        channelConfig,
                        audioFormat,
                        audioFormat,
                        AudioTrack.MODE_STREAM
                    )
                }
            } else {
                AudioTrack(
                    AudioManager.STREAM_MUSIC,
                    sampleRate,
                    channelConfig,
                    audioFormat,
                    9600,
                    AudioTrack.MODE_STREAM
                )
            }

        }

        fun playTestPcm() {
//            val s = File(context.getExternalFilesDir(""), "test.pcm").source()
//            viewModelScope.launch(Dispatchers.IO) {
//                pipe.sink.buffer().use {
//                    it.w
//                }
//
//            }
        }

    }

    @RequiresApi(Build.VERSION_CODES.Q)
    class WXAudioRecorder(
        val context: Context,
        val mediaProjection: MediaProjection,
        val audioOutputFile: File
    ) {

        private var mCodec: MediaCodec? = null
        private var mAudioRecord: AudioRecord? = null

        private val mMuxer by lazy {
            MediaMuxer(audioOutputFile.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
        }
        private val playbackConfig by lazy {
            AudioPlaybackCaptureConfiguration.Builder(mediaProjection)
                .addMatchingUsage(AudioAttributes.USAGE_MEDIA)
                .addMatchingUsage(AudioAttributes.USAGE_UNKNOWN)
                .addMatchingUsage(AudioAttributes.USAGE_GAME).build();
        }
    }


}